use std::{collections::BTreeMap, path::Path};

use serde::{Deserialize, Serialize};

use wtask_base::{error::WResult, export::{Local, NaiveDateTime, TimeZone, log_error}, logs::TIME_FORMAT_TIME};



pub(super) trait WTaskSubmitMetaTrait<T> {
    fn parse(&self) -> WResult<Vec<String>>;
    fn check(&self, value: T) -> WResult<Vec<String>>;
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypeFloat {
    pub(super) default: Option<f32>,
    pub(super) min: f32,
    pub(super) max: f32,
    pub(super) unit: f32
}

impl WTaskSubmitMetaTrait<f32> for WTaskSubmitMetaTypeFloat {
    fn parse(&self) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if self.max < self.min {
            errs.push("max < min".to_string());
        }
        if self.unit <= 0.0 {
            errs.push("unit <= 0.0".to_string());
        }
        if let Some(default) = self.default {
            let t = self.check(default)?;
            errs.extend(t);
        }
        Ok(errs)
    }

    fn check(&self, value: f32) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if value > self.max {
            errs.push(format!("value > max, max is {}", self.max));
        }
        if value < self.min {
            errs.push(format!("value < min, min is {}", self.min));
        }
        Ok(errs)
    }
}



#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypeInt {
    pub(super) default: Option<i32>,
    pub(super) min: i32,
    pub(super) max: i32,
    pub(super) unit: i32
}

impl WTaskSubmitMetaTrait<i32> for WTaskSubmitMetaTypeInt {
    fn parse(&self) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if self.max < self.min {
            errs.push("max < min".to_string());
        }
        if self.unit <= 0 {
            errs.push("unit <= 0".to_string());
        }
        if let Some(default) = self.default {
            let t = self.check(default)?;
            errs.extend(t);
        }
        Ok(errs)
    }

    fn check(&self, value: i32) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if value > self.max {
            errs.push(format!("value > max, max is {}", self.max));
        }
        if value < self.min {
            errs.push(format!("value < min, min is {}", self.min));
        }
        Ok(errs)
    }
}



#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypeString {
    pub(super) default: Option<String>,
    pub(super) min: usize,
    pub(super) max: usize,
    pub(super) multi: bool
}

impl WTaskSubmitMetaTrait<&str> for WTaskSubmitMetaTypeString {
    fn parse(&self) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if self.max < self.min {
            errs.push("max < min".to_string());
        }
        if self.min == 0 {
            errs.push("min need > 1".to_string());
        }
        if let Some(default) = &self.default {
            let t = self.check(default)?;
            errs.extend(t);
        }
        Ok(errs)
    }

    fn check(&self, value: &str) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if value.len() > self.max {
            errs.push(format!("value length > max, max is {}", self.max));
        }
        if value.len() < self.min {
            errs.push(format!("value length < min, min is {}", self.min));
        }
        Ok(errs)
    }
}



#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypePath {
    pub(super) default: Option<Vec<String>>,
    pub(super) is_dir: bool,
    pub(super) multi: bool
}

impl WTaskSubmitMetaTrait<&[&str]> for WTaskSubmitMetaTypePath {
    fn parse(&self) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if let Some(default) = &self.default {
            if default.is_empty() {
                errs.push("default is empty, do not set default if no default".to_string());
            } else {
                let default = default.iter().map(| v | v.as_str()).collect::<Vec<_>>();
                let t = self.check(&default)?;
                errs.extend(t);
            }
        }
        Ok(errs)
    }

    fn check(&self, value: &[&str]) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        for v in value {
            let p = Path::new(v);
            if !p.exists() {
                errs.push(format!("path not exist: {v}"));
                continue;
            }

            let t = if self.is_dir {
                p.is_dir()
            } else {
                p.is_file()
            };
            if t {
                continue;
            }
            errs.push(format!("path type error: is_dir {}", self.is_dir));
        }
        Ok(errs)
    }
}





#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypeDatetime {
    pub(super) default: Option<String>,
    pub(super) is_date: bool,
    pub(super) min: String,
    pub(super) max: String,
}

impl WTaskSubmitMetaTrait<&str> for WTaskSubmitMetaTypeDatetime {
    fn parse(&self) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        let min = if self.is_date {
            &format!("{} 00:00:00", self.min)
        } else {
            &self.min
        };
        let max = if self.is_date {
            &format!("{} 00:00:00", self.max)
        } else {
            &self.max
        };
        match (
            NaiveDateTime::parse_from_str(min, TIME_FORMAT_TIME),
            NaiveDateTime::parse_from_str(max, TIME_FORMAT_TIME)
        ) {
            (Ok(min), Ok(max)) => {
                let min = Local.from_local_datetime(&min).unwrap();
                let max = Local.from_local_datetime(&max).unwrap();
                if min > max {
                    errs.push("max < min".to_string());
                }
            },
            (min, max) => {
                if let Err(e) = min {
                    errs.push(format!("min fmt erro: {e:?}"));
                }
                if let Err(e) = max {
                    errs.push(format!("max fmt erro: {e:?}"));
                }
            }
        }
        if let Some(default) = &self.default {
            let t = self.check(default)?;
            errs.extend(t);
        }
        Ok(errs)
    }

    fn check(&self, value: &str) -> WResult<Vec<String>> {
        let min = if self.is_date {
            &format!("{} 00:00:00", self.min)
        } else {
            &self.min
        };
        let max = if self.is_date {
            &format!("{} 00:00:00", self.max)
        } else {
            &self.max
        };
        let value_now = if self.is_date {
            format!("{value} 00:00:00")
        } else {
            value.to_string()
        };
        let min = NaiveDateTime::parse_from_str(min, TIME_FORMAT_TIME).unwrap();
        let min = Local.from_local_datetime(&min).unwrap();
        let max = NaiveDateTime::parse_from_str(max, TIME_FORMAT_TIME).unwrap();
        let max = Local.from_local_datetime(&max).unwrap();

        let mut errs = Vec::new();
        if let Ok(date) = NaiveDateTime::parse_from_str(&value_now, TIME_FORMAT_TIME) {
            let date = Local.from_local_datetime(&date).unwrap();
            if date > max {
                errs.push(format!("value > max, max is {max}"));
            }
            if date < min {
                errs.push(format!("value < min, min is {min}"));
            }
        } else {
            errs.push(format!("value({value}) fmt error, need fmt {TIME_FORMAT_TIME}"));
        }
        Ok(errs)
    }
}





#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypeSelectChildren {
    pub(super) kv: BTreeMap<String, Vec<String>>,
    pub(super) args: WTaskSubmitMeta,
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct WTaskSubmitMetaTypeSelect {
    pub(super) default: Option<Vec<String>>,
    pub(super) min: usize,
    pub(super) max: usize,
    pub(super) custom: bool,
    pub(super) kv: BTreeMap<String, String>,
    pub(super) children: Option<WTaskSubmitMetaTypeSelectChildren>
}

impl WTaskSubmitMetaTrait<&[&str]> for WTaskSubmitMetaTypeSelect {
    fn parse(&self) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if self.max < self.min {
            errs.push("max < min".to_string());
        }
        if self.min == 0 {
            errs.push("min need > 0".to_string());
        }
        if let Some(default) = &self.default {
            let default = default.iter().map(| v | v.as_str()).collect::<Vec<_>>();
            let t = self.check(&default)?;
            errs.extend(t);
        }
        if (!self.custom) && self.kv.len() < self.max {
            errs.push("kv num not enough".to_string());
        }
        if let Some(children) = &self.children {
            if self.max == 1 && self.min == self.max {
                // check children
                // kv
                let mut args_id = Vec::new();
                for (k, v) in children.kv.iter() {
                    if children.kv.contains_key(k) {
                        args_id.extend(v);
                    } else {
                        errs.push(format!("children kv value {k} not exist"));
                    }
                }
                // args
                for &arg_id in args_id.iter() {
                    if let Some(arg) = children.args.get(arg_id) {
                        let t = arg.parse()?;
                        errs.extend(t);
                    } else {
                        errs.push(format!("children args id {arg_id} not exist"));
                    }
                }
                if args_id.len() != children.args.len() {
                    errs.push(format!(
                        "children kv has {} arg_id, but chileren args has {}",
                        args_id.len(),
                        children.args.len()
                    ));
                }

            } else {
                errs.push("children should be none".to_string());
            }
        }
        Ok(errs)
    }

    fn check(&self, value: &[&str]) -> WResult<Vec<String>> {
        let mut errs = Vec::new();
        if value.len() > self.max {
            errs.push(format!("value length > max, max is {}", self.max));
        }
        if value.len() < self.min {
            errs.push(format!("value length < min, min is {}", self.min));
        }
        for &i in value {
            if self.kv.contains_key(i) || self.custom {
                continue;
            }
            errs.push(format!("value {i} not exist"));
        }
        Ok(errs)
    }
}










#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) enum WTaskSubmitMetaType {
    NumberFloat(WTaskSubmitMetaTypeFloat),
    NumberInt(WTaskSubmitMetaTypeInt),
    String(WTaskSubmitMetaTypeString),
    Datetime(WTaskSubmitMetaTypeDatetime),
    Path(WTaskSubmitMetaTypePath),
    Select(WTaskSubmitMetaTypeSelect)
}


#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WTaskSubmitMetaBase {
    pub(super) name: String,
    pub(super) doc: String,
    pub required: bool,
    pub(super) attrs: WTaskSubmitMetaType,
}

impl WTaskSubmitMetaBase {
    pub(super) fn parse(&self) -> WResult<Vec<String>> {
        let mut t = match &self.attrs {
            WTaskSubmitMetaType::NumberFloat(data) => data.parse(),
            WTaskSubmitMetaType::NumberInt(data) => data.parse(),
            WTaskSubmitMetaType::Datetime(data) => data.parse(),
            WTaskSubmitMetaType::String(data) => data.parse(),
            WTaskSubmitMetaType::Path(data) => data.parse(),
            WTaskSubmitMetaType::Select(data) => data.parse(),
        }?;
        for tt in t.iter_mut() {
            *tt = format!("Arg {} error: {}", self.name, tt);
            log_error!("{tt}");
        }
        Ok(t)
    }

    pub(super) fn default(&self) -> Option<String> {
        match &self.attrs {
            WTaskSubmitMetaType::NumberFloat(data) => {
                data.default.map(|default| format!("{default}"))
            },
            WTaskSubmitMetaType::NumberInt(data) => {
                data.default.map(|default| format!("{default}"))
            },
            WTaskSubmitMetaType::Datetime(data) => data.default.clone(),
            WTaskSubmitMetaType::String(data) => data.default.clone(),
            WTaskSubmitMetaType::Path(data) => {
                data.default.as_ref().map(|default| default.join("@@@").to_string())
            },
            WTaskSubmitMetaType::Select(data) => {
                data.default.as_ref().map(|default| default.join("@@@").to_string())
            },
        }
    }

    pub fn check(&self, value: &str) -> WResult<Vec<String>> {
        match &self.attrs {
            WTaskSubmitMetaType::NumberFloat(data) => data.check(value.parse::<f32>()?),
            WTaskSubmitMetaType::NumberInt(data) => data.check(value.parse::<i32>()?),
            WTaskSubmitMetaType::Datetime(data) => data.check(value),
            WTaskSubmitMetaType::String(data) => data.check(value),
            WTaskSubmitMetaType::Path(data) => {
                let s = value.split("@@@").collect::<Vec<_>>();
                data.check(&s)
            },
            WTaskSubmitMetaType::Select(data) => {
                let s = value.split("@@@").collect::<Vec<_>>();
                data.check(&s)
            },
        }
    }
}


pub(crate) type WTaskSubmitMeta = BTreeMap<String, WTaskSubmitMetaBase>;

